#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/input.h>

typedef struct Album Album_t;
typedef struct Album *pAlbum_t;

//相片组成
struct Album
{
    const char *path;
    int offsize_y;
    int offsize_x;
    pAlbum_t prev;
    pAlbum_t next;
};

// 有空节点的链表
// 头节点不存放数据
pAlbum_t Album_Link_List_Init()
{
    pAlbum_t phead = ( pAlbum_t )malloc( sizeof( Album_t ));
    if ( phead == NULL )
    {
        perror("Init head node fail");
        return phead;
    }
    phead->path = NULL;
    phead->offsize_x = 0;
    phead->offsize_y = 0;
    phead->prev = phead;
    phead->next = phead;

    return phead;
}

pAlbum_t Create_new_node( const char *path, int offset_x, int offset_y )
{
    pAlbum_t pnew = (pAlbum_t)malloc(sizeof(Album_t));
    if ( pnew == NULL )
    {
        perror("malloc a new node fail");
        return NULL;
    }

    pnew->path = path;
    pnew->offsize_x = offset_x;
    pnew->offsize_y = offset_y;
    pnew->prev = NULL;
    pnew->next = NULL;
    return pnew;    
}

// 头插
#if 1
void Album_Link_List_Head_Insert(pAlbum_t phead, pAlbum_t node)
{
    pAlbum_t plink = phead;
    if (node == NULL)
    {
        perror("Head insert a node fail");
        return;
    }
    // 判断是否有首节点
    if (phead->next == phead&&phead->prev==phead)
    {
        plink->prev = node;
        plink->next = node;
        node->prev = phead;
        node->next = phead;
        return;
    }
    // 头插
    //我的头结点的pre不需要动，next需要动
    //插入的node节点prev和next都需要动
    //前节点的prev需要移动到插入的节点上   
    node->next = phead->next;
    node->prev = phead;
    phead->next->prev=node;
    phead->next = node;
    
}
#endif



// 遍历链表
#if 1
void Print_Album_Link_List_next(pAlbum_t phead)
{
    pAlbum_t plink = phead->next;

    if (plink == phead)
    {
        perror("None Link List");
        return;
    }

    while (plink != phead)
    {
        printf("PATH:%s  OFFSIZE_X:%d  OFFSIZE_Y:%d  \n", plink->path, plink->offsize_x, plink->offsize_y);
        plink = plink->next;
    }
    printf("\n");
}
#endif


#if 1
// 释放链表
void Release_Album_Link_list(pAlbum_t phead)
{
    pAlbum_t plink = phead->next;
    pAlbum_t ptemp = NULL;

    //不只有头结点的情况
    while (plink != phead)
    {
        ptemp = plink->next;
        free(plink);
        plink = ptemp;
    }
    free(phead);
    
    printf("链表已经全部释放\n");
}
#endif

// 按钮部分
int red = 0x00ff0000;
int green = 0x0000ff00;
int blue = 0x000000ff;


int show_circle(int *addr)
{
    //bg_color
    int white = 0x00ffffff;

    //直接通过mmap这个函数映射的地址来给LCD刷颜色
    int x = 0; //横轴
    int y = 0; //纵轴
    for(y=0;y<480;y++)
    {
        //通过映射的地址来指向LCD上每一个像素点，然后解引用赋值
        for(x=0;x<800;x++)
        {
            //背景色刷白色
            *(addr+y*800+x) = white;

            //画了一个圆心是(400,240)半径是30的圆面
            if((x-600)*(x-600)+(y-150)*(y-150) <= 1600)
                *(addr+y*800+x) = red;
            
            //画另外一个圆
            if((x-600)*(x-600)+(y-330)*(y-330) <= 1600)
                *(addr+y*800+x) = green; 
        }
    }    
   
}




int show_bmp_size(int *addr,const char *bmp_path,int offset_x,int offset_y)
{
    //打开BMP图片
    //标准IO，打开BMP图片
    FILE *fp_bmp = fopen(bmp_path, "rb");
    if(fp_bmp == NULL)
    {
        printf("fopen bmp fail\n");
        return -1;
    }

    //保证读取图片的光标在开头
    fseek(fp_bmp, 0 ,SEEK_SET);

    //读取BMP图片的的大小
    int width = 0;//宽度
    int height = 0;//高度
    fseek(fp_bmp,18,SEEK_SET);
    fread(&width, 4, 1, fp_bmp);
    fread(&height, 4, 1, fp_bmp);
    printf("width:%d height:%d\n", width, height);

    //读取bmp图片的数据
    fseek(fp_bmp, 54, SEEK_SET);
    char *pbuf = (char *)malloc(sizeof(char) * width * height * 3);
    fread(pbuf, 1, width*height*3, fp_bmp);

    //bmp图片的起始位置坐标(offset_x,offset_y)
    // int offset_x = 100;
    // int offset_y = 100;

    int x = 0; //横轴
    int y = 0; //纵轴
    for(y=0;y<height;y++)
    {
        //通过映射的地址来指向LCD上每一个像素点，然后解引用赋值
        for(x=0;x<width;x++)
        {   // 计算 LCD 坐标，确保在屏幕范围内（0<=x<800, 0<=y<480）
            int lcd_x = offset_x + x;
            int lcd_y = offset_y + (height - 1 - y);
            if (lcd_x < 0 || lcd_x >= 800 || lcd_y < 0 || lcd_y >= 480)
                continue;  // 超出屏幕的像素不绘制
            //LCD的最后一行 = BMP的第一行
            *(addr+(offset_y+height-1-y)*800+offset_x+x) = pbuf[(y*width+x)*3+0] |
                                                        pbuf[(y*width+x)*3+1]<<8 |
                                                        pbuf[(y*width+x)*3+2]<<16;
        }
    }

    //关闭BMP文件、
    fclose(fp_bmp);
    free(pbuf);

}

int show_color(int *addr,int color)
{
   //black 0x00000000

    //直接通过mmap这个函数映射的地址来给LCD刷颜色
    int x = 0; //横轴
    int y = 0; //纵轴
    for(y=0;y<480;y++)
    {
        //通过映射的地址来指向LCD上每一个像素点，然后解引用赋值
        for(x=0;x<800;x++)
            *(addr+y*800+x) = color;
    }

}

//获取触摸屏坐标
int Get_Slide_or_CLICK(int *x, int *y)
{

    #if 1
    //触摸屏滑动
        //打开触摸屏文件
        int fd_ts = open("/dev/input/event0", O_RDWR);
        if (fd_ts < 0)
        {
            perror("open /dev/input/event0 fail");
            exit(1);
        }
        struct input_event ts;

     
        
        //抓取按下值
        while(1)
        {   
            memset(&ts, 0, sizeof(struct input_event));
            read(fd_ts, &ts, sizeof(struct input_event));
            //按下时，计算X的坐标
            if(ts.type == EV_ABS && ts.code == ABS_X)
            {
                *x = ts.value * 800 / 1024;
            }

            //按下时，计算Y坐标
            if(ts.type == EV_ABS && ts.code == ABS_Y)
            {
                *y = ts.value * 480 / 600;
            }

            //按下时的坐标
            if(ts.type == EV_KEY && ts.value == 1 && ts.code == BTN_TOUCH)
            {
                printf("X=%d  Y=%d \n", *x, *y);
                break;
            }
            
        }
       
        //int x1=0,y1=0;
        // //抓取松开值
        // while(1)
        // {   
        //     memset(&ts, 0, sizeof(struct input_event));
        //     read(fd_ts, &ts, sizeof(struct input_event));
        //     //松开时，计算X的坐标
        //     if(ts.type == EV_ABS && ts.code == ABS_X)
        //     {
        //         x1 = ts.value * 800 / 1024;
        //     }

        //     //松开时，计算Y坐标
        //     if(ts.type == EV_ABS && ts.code == ABS_Y)
        //     {
        //         y1 = ts.value * 480 / 600;
        //     }

        //     //松开时的坐标
        //     if(ts.type == EV_KEY && ts.value == 0 && ts.code == BTN_TOUCH)
        //     {
        //         printf("X1=%d  Y1=%d \n", x1, y1);
        //         break;
        //     }

        //}

        //判断左右
        //可以给x与x1,y与y1做差，排除误触的可能
        // if(x1 == 0) printf("CLICK\n");    
        // else if(*x > x1 && *y == y1) printf("正左划\n");
        // else if(*x > x1 && *y > y1) printf("左上划\n");
        // else if(*x > x1 && *y < y1) printf("左下划\n");
        // else if(*x < x1 && *y == y1) printf("正右划\n");
        // else if(*x < x1 && *y > y1) printf("右上划\n");
        // else  printf("右下划\n");
     #endif   
}
    


//循环播放
void Cycle_Display_Album(int *addr, pAlbum_t phead)
{
    //没有节点的情况
    if(phead->next == phead)
    {
        printf("相簿中没有相册");
        return;
    }

    pAlbum_t plink = phead->next;

    //屏幕先显示黑色
    show_color(addr, 0xff000000);

    while(1)
    {   //头结点不需要播放
        if(plink == phead)
        {
            sleep(3);
            plink = phead->next;
        }

        //照片展示
        show_bmp_size(addr, plink->path, plink->offsize_x, plink->offsize_y);
        sleep(1);
        printf("success");

        plink = plink->next;
    }
    

}


//上一张 下一张图片
void choice_image(int *addr, pAlbum_t phead){
    int x=0, y=0;

    //屏幕先显示黑色
    show_color(addr, 0xff000000);
    //绘制两个按钮
    show_circle(addr);
   
     //没有节点的情况
    if(phead->next == phead)
    {
        printf("相簿中没有相册");
        return;
    }
    pAlbum_t plink = phead->next;


    while(1)
    {
            Get_Slide_or_CLICK(&x, &y);
            //在按钮有效范围内点击则有效
            if( ((x-600)*(x-600)+(y-150)*(y-150))<=1600 )
            {
                plink = plink->prev;
                if(plink == phead)
                {
                    plink = phead->prev;
                }
                printf("上一张");
                show_bmp_size(addr, plink->path, plink->offsize_x, plink->offsize_y);
            }
            else if( (x-600)*(x-600)+(y-330)*(y-330) <= 1600 )
            {
                plink = plink->next;
                 if(plink == phead)
                {
                    plink = phead->next;
                }
                printf("下一张");
                show_bmp_size(addr, plink->path, plink->offsize_x, plink->offsize_y);
            }

    }

}

int main(int argc, char **argv)
{
    //空节点 
    pAlbum_t phead = Album_Link_List_Init();
    //插入
    Album_Link_List_Head_Insert(phead, Create_new_node("dr&md1.bmp", 0, 80));
    Album_Link_List_Head_Insert(phead, Create_new_node("dr&md2.bmp", 0, 80));
    Album_Link_List_Head_Insert(phead, Create_new_node("dr&md3.bmp", 0, 80));
    Album_Link_List_Head_Insert(phead, Create_new_node("dr&md4.bmp", 0, 80));
    Album_Link_List_Head_Insert(phead, Create_new_node("dr&md5.bmp", 0, 80));
    Album_Link_List_Head_Insert(phead, Create_new_node("dr&md6.bmp", 0, 80));


    //打开LCD文件
    int fd_lcd = open("/dev/fb0",O_RDWR);
    if(fd_lcd < 0)
    {
        printf("open lcd fail\n");
        return -1;
    }

    //LCD的映射
    int *addr = mmap(NULL, //If addr is NULL, then the kernel chooses the address
                     800*480*4, //LCD的大小
                     PROT_READ|PROT_WRITE,//LCD的权限
                     MAP_SHARED,//标志量
                     fd_lcd, //LCD的文件描述符
                     0); //LCD的偏移量

    if(addr == MAP_FAILED)
    {
        printf("mamp fail\n");
        return -1;
    }

    choice_image(addr, phead);

    Print_Album_Link_List_next(phead);
    Release_Album_Link_list(phead);

    return 0;
}